package com.chenhy.server.utils;

import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtTokenUtil {

    // JWT储存有效信息的Key值
    private static final String CLAIM_KEY_USERNAME = "sub";

    // JWT创建时间的key值
    private static final String CLAIM_KEY_CREATED = "created";

    // JWT密钥
    @Value("${jwt.secret}")
    private String secret;

    // 失效时间
    @Value("${jwt.expiration}")
    private Long expiration;


    // 根据传入的用户信息生成Token
    public String generateToken(UserDetails userDetails){
        // 构建需要存放到JWT的荷载
        Map<String,Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        claims.put(CLAIM_KEY_CREATED, new Date());
        // 调用另外的方法传入荷载生成JWT
        return generateToken(claims);
    }

    private String generateToken(Map<String, Object> claims){
        return Jwts.builder().setClaims(claims)
                .setExpiration(generateTokenExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    // 计算Token的失效时间，因为我们配置的失效时间是一个时间段，
    // 我们需要通过当前时间加上该时间段来算出该Token在什么时候失效
    private Date generateTokenExpirationDate(){
        return new Date(System.currentTimeMillis()+expiration*1000);
    }

    // 从token中解析出登录的用户名
    public String getUserNameFromToken(String token){
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            // 获取荷载中key为sub的值（详见源码）
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    // 根据传入的token解析出荷载（储存的数据）
    private Claims getClaimsFromToken(String token){
        Claims claims = null;

        try {
            claims = Jwts.parser()
                   .setSigningKey(secret)
                   .parseClaimsJws(token)
                   .getBody();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return claims;
    }

    // 判断Token是否有效
    public Boolean validateToken(String token, UserDetails userDetails){
        // 需判断Token是否过期和Token里的用户名是否与UserDetails里的一致
        String username = getUserNameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }

    // 判断token是否过期失效
    private Boolean isTokenExpired(String token){
        Date expiredDate = getExpiredDataFromToken(token);
        return expiredDate.before(new Date());
    }

    // 获取传入Token的过期时间
    private Date getExpiredDataFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        // 获取过期时间
        return claims.getExpiration();
    }

    // 判断Token能否被刷新（就是判断Token是否失效，如果失效就不能刷新）
    public boolean canRefresh(String token){
        return !isTokenExpired(token);
    }

    // 刷新Token（其实就是先解析出荷载，然后修改荷载中的创建时间，然后重新调用生成token的方法重新生成一个Token）
    // Claims其实就是一个map，和我们生成Token时传入的Map一样，只不过从Token中解析出来时在里面再放了点东西
    public String refreshToken(String oldToken){
        Claims claims = getClaimsFromToken(oldToken);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }


}
